<template>
  <XhhTable
      ref="table"
      :columns="columns"
      :api="api"
      :form-config="formConfig"
      :params="formConfig.form"
      unique-id="resource-list-table"
      stripe
      checkbox
      default-expand-all
      row-key="id"
      :setAddDialog="setAddDialog"
      :row-class-name="handleRowClassName"
      :importConfig="importConfig"
      :insertToolList="insertToolList"
      @delete="handleDelete"
      @del="handleDel"
      @disableRow="handleDisableRow"
      @enableRow="handleEnableRow"
      @export="handleExport"
      @addSub="handleAddSub"
      @sort="handleSort"
  ></XhhTable>
  <XhhDialog
      v-model="dialogVisible"
      title="新增子资源"
      width="700px"
      @confirm="handleConfirmAddSub"
  >
    <XhhForm
        :schema="formConfig.dialogSchema"
        v-model="dialogFormValue"
        inline
        v-bind="formConfig.dialogFormProps"
        :showFormButton="false"
        :defaultSearch="false"
        :isGrid="true"
    ></XhhForm>
  </XhhDialog>
  <XhhDialog
      v-model="sortDialogVisible"
      title="调整位置"
      width="700px"
      customCancel
      :before-close="handleBeforeClose"
      @confirm="handleConfirmSort"
      @cancel="handleCancelSort"
  >
    <ElTree
        :data="menuTreeList"
        draggable
        default-expand-all
        node-key="id"
        @node-drop="handleDrop"
    >
      <template #default="{ node, data }">
        <span>
          {{ node.label }}
          <span class="pl10"></span>
          {{ data.sortNumber }}
        </span>
      </template>
    </ElTree>
  </XhhDialog>
</template>
<script setup>
import {ref} from 'vue'
import req from '@/utils/req/axios'
import {ElTree} from 'element-plus'
import {tip, tipBox} from '@tip'
import {requiredRule} from '@/utils/common/form'
import urls from '@/utils/constants/urls.js'
import {downloadFile} from '@/utils/common/window/index.js'
import {resourceTypeList, showOverflowTooltip} from '@/utils/constants/utils'
import {destName} from '@/utils/common/fns'
import {
  buildTree,
  compareSortMap,
  getListByTree,
  getSortMap,
  sortList,
} from '@/utils/common/fns/array'
import useGlobalStore from '@/stores/global.js'
import {cloneDeep} from 'lodash-unified'

const globalStore = useGlobalStore()

const table = ref()
const insertToolList = [
  {
    prop: 'sort',
    label: '调整位置',
  },
]

const columns = ref([
  {
    prop: 'label',
    label: '资源名',
  },
  {
    prop: 'uniqueProp',
    label: '唯一属性',
    ...showOverflowTooltip,
  },
  {
    prop: 'parentUniqueProp',
    label: '唯一父属性',
    ...showOverflowTooltip,
  },
  {
    prop: 'type',
    label: '资源类型',
    width: 100,
    formatter(row) {
      return destName(resourceTypeList, row.type)
    },
  },
  {
    prop: 'op',
    label: '操作',
    buttons({row}) {
      return [
        {
          prop: 'addSub',
          label: '新增子资源',
          type: 'primary',
        },
        'delete',
        'edit',
        'view',
        {
          // status 1 表示已启用 其他表示已停用
          prop: row.status === '1' ? 'disableRow' : 'enableRow',
          label: row.status === '1' ? '停用' : '启用',
          class: row.status === '1' ? 'red' : 'green',
        },
      ]
    },
    width: 340,
  },
])
const originData = ref([])
const sortDialogVisible = ref(false)
const sortableMenuList = ref([])
const originSortableMenuList = ref([])
const menuTreeList = ref([])
const originSortMap = ref({})
const updatedList = ref([]) // 每次操作的修改内容都保存在这里
const api = {
  async request(params) {
    const {total, data} = await req.post(urls.v1.system.resource.list, {
      ...params,
      status: '',
    })
    originData.value = data
    return {
      total,
      data: buildTree(data),
    }
  },
}
const formConfig = {
  form: {},
  schema: [
    {
      prop: 'name',
      label: '资源名',
    },
    {
      prop: 'uniqueProp',
      label: '唯一属性',
    },
    {
      prop: 'parentUniqueProp',
      label: '唯一父属性',
    },
    {
      prop: 'type',
      label: '资源类型',
      comp: 'select',
      props: {
        options: resourceTypeList,
        style: 'width: 140px;',
      },
    },
  ],
  queryFormProps: {},
  // 如果配置了dialogForm，则新增按钮会自动弹出
  dialogForm: {},
  dialogSchema: [
    {
      prop: 'label',
      label: '资源名',
    },
    {
      prop: 'uniqueProp',
      label: '唯一属性',
    },
    {
      prop: 'parentUniqueProp',
      label: '唯一父属性',
    },
    {
      prop: 'type',
      label: '资源类型',
      comp: 'select',
      props: {
        options: resourceTypeList,
      },
    },
  ],
  dialogFormProps: {
    // dev13及以后可以删除此属性
    defaultSearch: false,
    rules: {
      ...requiredRule('label'),
      ...requiredRule('uniqueProp'),
    },
  },
  dialogProps: {
    async onConfirm() {
      // todo 验证表单
      await table.value.validDialogForm()
      // 如果验证失败，直接就在内部抛出错误了，下边的代码不会走了
      // 获取表单数据
      // 整理表单数据
      let url
      // 发送请求
      if (table.value.dialogFormMode === 'add') {
        // 新增
        url = urls.v1.system.resource.add
      } else if (table.value.dialogFormMode === 'edit') {
        // 编辑
        url = urls.v1.system.resource.update
      }
      // 表单数据
      const params = {
        ...table.value.dialogForm,
      }
      await req.post(url, params)
      // 关闭弹窗
      table.value.closeFormDialog()
      // 刷新列表
      table.value.searchData(true)
      await handleRefreshMenu()
    },
  },
}

const importConfig = {
  api: {
    async requestTemplate() {
      const res = await req.post(
          urls.v1.system.resource.exportTemplate,
          {},
          {
            responseType: 'blob',
          },
      )
      return res.data
    },
    requestImport(formData, headers) {
      return req.post(urls.v1.system.resource.import, formData, {
        headers,
        _isReturnOrigin: true,
      })
    },
  },
}

async function updateSort(params, config) {
  await req.post(urls.v1.system.resource.changeTree, {changeList: params}, config || {})
}

function setAddDialog({props}) {
  props.dialogForm.value = {
    type: '1', // 类型默认值为 1页面 可选值 2为按钮
  }
}

const dialogVisible = ref(false)
const dialogFormValue = ref({})

function handleAddSub({row}) {
  dialogFormValue.value = {
    parentUniqueProp: row.uniqueProp,
    type: '1', // 类型默认值为 1页面 可选值 2为按钮
  }
  dialogVisible.value = true
}

async function handleSort() {
  console.log('handleSort')
  const data = originData.value
  originSortMap.value = getSortMap(data)
  // 对数据进行树形处理
  const treeData = buildTree(data, {autoGenSortNumber: true})
  // 树形数据带着sortNumber，再将树形数据转为数组数据
  sortableMenuList.value = getListByTree(treeData)
  originSortableMenuList.value = cloneDeep(sortableMenuList.value)
  // 根据比较结果
  // const result = compareSortMap(originSortMap.value, getSortMap(sortableMenuList.value))
  // if (result) {
  //   await updateSort(Object.values(result), {_showSuccessTip: false})
  // }
  menuTreeList.value = treeData
  updatedList.value = []
  sortDialogVisible.value = true
}

async function handleCancelSort() {
  await handleBeforeClose(() => {
    sortDialogVisible.value = false
  })
}

async function handleBeforeClose(done) {
  const updatedParams = getUpdatedParams()
  if (updatedParams.length) {
    const confirm = await tipBox.confirm('数据变化还未保存，是否确认关闭', '提示')
    if (confirm === 'confirm') {
      done()
    }
  } else {
    done()
  }
}

async function handleConfirmSort() {
  console.log('handleConfirmSort')
  if (updatedList.value.length) {
    // 提取更新数据，并与原始数据对比
    const updatedParams = getUpdatedParams()
    if (updatedParams.length) {
      await updateSort(updatedParams)
      updatedList.value = []
      table.value.searchData(true)
      await handleRefreshMenu()
      sortDialogVisible.value = false
      return
    }
  }
  tip({message: '没有检测到数据变化'})
}

/**
 * 拖拽结束
 * @param draggingNode
 * @param dropNode
 * @param type 可能的值 after before inner
 */
async function handleDrop(draggingNode, dropNode, type) {
  console.log('handleDrop')
  let confirm = 'ok'
  let levelChanged =
      (draggingNode.data.parentUniqueProp === dropNode.data.parentUniqueProp && type === 'inner') ||
      draggingNode.data.parentUniqueProp !== dropNode.data.parentUniqueProp
  let destList = []
  let isInDestList = false
  if (levelChanged) {
    try {
      confirm = await tipBox.confirm('确认要改变菜单层级结构吗？', '提示')
    } catch {
      confirm = 'cancel'
    }
  }
  let updateList = []
  if (confirm === 'ok' || confirm === 'confirm') {
    if (confirm === 'confirm') {
      // 结构发生了改变
      isInDestList = false
      destList = cloneDeep(
          sortableMenuList.value.filter(
              (item) =>
                  item.parentUniqueProp ===
                  (type === 'inner' ? dropNode.data.uniqueProp : dropNode.data.parentUniqueProp),
          ),
      )
      const draggingItem = sortableMenuList.value.find((item) => item.id === draggingNode.data.id)
      draggingItem.parentUniqueProp =
          type === 'inner' ? dropNode.data.uniqueProp : dropNode.data.parentUniqueProp
      updateList.push({
        id: draggingItem.id,
        label: draggingItem.label,
        parentUniqueProp: draggingItem.parentUniqueProp,
      })
    } else {
      updateList.push({id: draggingNode.data.id, label: draggingNode.data.label})
      // 只调整顺序
      isInDestList = true
      destList = cloneDeep(
          sortableMenuList.value.filter(
              (item) => item.parentUniqueProp === dropNode.data.parentUniqueProp,
          ),
      )
    }
    let dropIndex = destList.findIndex((item) => item.id === dropNode.data.id)
    let resultList = []
    let crtSortNumber = 0
    if (type === 'inner') {
      // 插入顺序
      crtSortNumber = destList.length + 1
    }
    if (isInDestList) {
      const originIndex = destList.findIndex((item) => item.id === draggingNode.data.id)
      let betweenList
      if (originIndex < dropIndex) {
        betweenList = destList.slice(originIndex + 1, type === 'before' ? dropIndex : dropIndex + 1)
        crtSortNumber = type === 'before' ? dropIndex : dropIndex + 1
        resultList = betweenList.map((item) => ({
          ...item,
          sortNumber: item.sortNumber - 1,
        }))
      } else {
        betweenList = destList.slice(type === 'before' ? dropIndex : dropIndex + 1, originIndex)
        crtSortNumber = type === 'before' ? dropIndex + 1 : dropIndex + 2
        resultList = betweenList.map((item) => ({
          ...item,
          sortNumber: item.sortNumber + 1,
        }))
      }
    } else {
      resultList = [
        ...sortableMenuList.value
            .filter(
                (item) =>
                    item.parentUniqueProp === draggingNode.data.parentUniqueProp &&
                    item.sortNumber > draggingNode.data.sortNumber,
            )
            .map((item) => ({
              ...item,
              sortNumber: item.sortNumber - 1,
            })),
        ...(type === 'inner'
            ? []
            : destList.slice(type === 'before' ? dropIndex : dropIndex + 1).map((item) => ({
              ...item,
              sortNumber: item.sortNumber + 1,
            }))),
      ]
      if (type !== 'inner') {
        crtSortNumber = type === 'before' ? dropIndex + 1 : dropIndex + 2
      }
    }

    updateList[0].sortNumber = crtSortNumber
    updateList.push(
        ...resultList.map((item) => ({
          id: item.id,
          label: item.label,
          sortNumber: item.sortNumber,
        })),
    )
    sortableMenuList.value.forEach((item) => {
      const updateItem = updateList.find((up) => up.id === item.id)
      if (updateItem) {
        item.sortNumber = updateItem.sortNumber
      }
    })
    // 根据修改之后的sortNumber进行排序
    sortableMenuList.value = sortList(sortableMenuList.value)
    updatedList.value.push(updateList)
  }
  // 保持树的数据与列表数据同步
  menuTreeList.value = buildTree(sortableMenuList.value)
}

function getUpdatedParams() {
  const updatedResult = []
  updatedList.value.forEach((itemList) => {
    itemList.forEach((item) => {
      const upItem = updatedResult.find((up) => up.id === item.id)
      if (upItem) {
        upItem.sortNumber = item.sortNumber
        if (item.parentUniqueProp !== undefined) {
          upItem.parentUniqueProp = item.parentUniqueProp
        }
      } else {
        updatedResult.push(item)
      }
    })
  })
  // 将 updatedResult 与原始数据对比，如果没有发生改变，则返回false
  const result = []
  originSortableMenuList.value.forEach((item) => {
    const resultItem = updatedResult.find((up) => up.id === item.id)
    if (resultItem) {
      if (resultItem.parentUniqueProp !== undefined) {
        if (
            resultItem.parentUniqueProp !== item.parentUniqueProp ||
            resultItem.sortNumber !== item.sortNumber
        ) {
          result.push(resultItem)
        }
      } else {
        if (resultItem.sortNumber !== item.sortNumber) {
          result.push(resultItem)
        }
      }
    }
  })
  return result
}

async function handleRefreshMenu() {
  const res = await req.post(urls.v1.system.resource.list)
  const menuList = buildTree(
      res.data.map((item) => ({...item, index: item.uniqueProp, title: item.label})),
  )
  globalStore.setMenuList(menuList)
}

async function handleConfirmAddSub() {
  await req.post(urls.v1.system.resource.add, dialogFormValue.value)
  dialogVisible.value = false
  // 刷新列表
  table.value.searchData(true)
  await handleRefreshMenu()
}

/**
 * 单个删除
 * @param row
 * @returns {Promise<void>}
 */
async function handleDelete({row}) {
  await req.post(urls.v1.system.resource.del, {id: row.id})
  table.value.searchData(true)
  await handleRefreshMenu()
}

/**
 * 批量删除
 * @returns {Promise<void>}
 */
async function handleDel() {
  if (!table.value.checkedRows?.length) {
    tip({message: '请选择要删除的行', type: 'error'})
    return
  }
  await req.post(urls.v1.system.resource.del, {
    ids: table.value.checkedRows.map((item) => item.id),
  })
  table.value.searchData()
  await handleRefreshMenu()
}

/**
 * 导出excel
 * @returns {Promise<void>}
 */
async function handleExport() {
  const dataLength = table.value.checkedRows?.length
  // 若存在选中的数据，则导出选中的数据，否则按条件导出数据
  const confirm = await tipBox.confirm(
      dataLength ? `确认要导出选中的${dataLength}条数据吗？` : `确认要根据查询条件导出数据吗？`,
      '提示',
      {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      },
  )
  if (confirm === 'confirm') {
    // 查询条件
    const res = await req.post(
        urls.v1.system.resource.export,
        {
          ...(dataLength
              ? {ids: table.value.checkedRows.map((item) => item.id)}
              : {...table.value.queryForm}),
        },
        {responseType: 'blob'},
    )
    // 将流转化为文件进行下载 todo 文件名称从后端获取，从headers中获取
    downloadFile({blob: res.data, fileName: '资源列表.xlsx'})
  }
}

async function handleStatusRow({row, status, message, label}) {
  const confirm = await tipBox.confirm(`确认要执行${label}操作吗？`, '提示', {
    confirmButtonText: '确认',
    cancelButtonText: '取消',
    type: 'warning',
  })
  if (confirm === 'confirm') {
    await req.post(
        urls.v1.system.resource.update,
        {id: row.id, status},
        {_showSuccessTip: false},
    )
    tip({message, type: 'success'})
    table.value.searchData(true)
    await handleRefreshMenu()
  }
}

async function handleDisableRow({row}) {
  await handleStatusRow({row, status: '2', message: '停用成功', label: '停用'})
}

async function handleEnableRow({row}) {
  await handleStatusRow({row, status: '1', message: '启用成功', label: '启用'})
}

function handleRowClassName({row}) {
  return row.status === '1' ? 'enable-row' : 'disable-row'
}
</script>
